package com.lahiyam.study.mybatis.util;

import java.io.IOException;
import java.io.InputStream;

import javax.sql.DataSource;

import org.apache.commons.lang3.RandomUtils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.stdout.StdOutImpl;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory;

import com.lahiyam.study.mybatis.config.MyDataSourceFactory;
import com.lahiyam.study.mybatis.mapper.RoleAnnoMapper;
import com.lahiyam.study.mybatis.mapper.RoleMapper;
import com.lahiyam.study.mybatis.mapper.SqlMapper;

import lombok.extern.slf4j.Slf4j;

/**
 * SqlSessionFactory辅助类 负责：用SqlSessionFactoryBuilder来创建SqlSessionFactory，单例对象。 负责： 打开会话
 *
 * @author U2R2otkit
 * @version 2018-11-03 12:22:00
 */
@Slf4j
public class SqlSessionFactoryUtil
{
    /**
     * SqlSessionFactory对象
     */
    private static SqlSessionFactory sqlSessionFactory = null;
    
    /**
     * 类线程锁
     */
    private static final Class<?> CLASS_LOCK = SqlSessionFactoryUtil.class;
    
    /**
     * 构造函数 为了实现单例，这个必须构造函数私有化。
     */
    private SqlSessionFactoryUtil()
    {
    }
    
    /**
     * 构建SqlSessionFactory <br>
     * 用SqlSessionFactoryBuilder的build方法来构造SqlSessionFactory，该方法的参数是MyBatis的主配置文件。
     *
     * @return
     */
    public static void initSqlSessionFactory()
    {
        try (InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"))
        {
            synchronized (CLASS_LOCK)
            {
                if (sqlSessionFactory == null)
                {
                    sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
                    log.info("初始化SqlSessionFactory成功...");
                }
            }
        }
        catch (IOException e)
        {
            log.error("初始化SqlSessionFactory失败, {}", e.getMessage());
        }
    }
    
    /**
     * mybatis-config.xml等价Java代码配置
     * 
     * @return
     * @see [类、类#方法、类#成员]
     */
    public static void initSqlSessionFactoryInJava()
    {
        log.info("初始化initSqlSessionFactoryInJava ...");
        DataSource dataSource = MyDataSourceFactory.getDataSource();
        TransactionFactory transactionFactory = new JdbcTransactionFactory();
        Environment environment = new Environment("development", transactionFactory, dataSource);
        Configuration configuration = new Configuration(environment);
        configuration.setCacheEnabled(true);
        configuration.setLazyLoadingEnabled(true);
        configuration.setMultipleResultSetsEnabled(true);
        configuration.setUseColumnLabel(true);
        configuration.setDefaultExecutorType(ExecutorType.REUSE);
        configuration.setDefaultStatementTimeout(25000);
        configuration.setLogImpl(StdOutImpl.class);
        configuration.getTypeAliasRegistry().registerAliases("com.lahiyam.study.mybatis.entity");
        
        // 方式1,只能选择1种
        configuration.addMapper(RoleAnnoMapper.class);
        configuration.addMapper(RoleMapper.class);
        configuration.addMapper(SqlMapper.class);
        
        // 方式2
        // configuration.addMappers("com.lahiyam.study.mybatis.mapper");
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);
    }
    
    /**
     * 打开SqlSession
     *
     * 作用： 返回SqlSession <br>
     * 1. 先判断单例对象sqlSessionFactory是否被创建 <br>
     * 2. 如果已经创建，就直接调用sqlSessionFactory的openSession方法来创建一个SqlSession，并返回。 <br>
     * 3. 如果没有创建，就调用对用的方法创建sqlSessionFactory，然后调用sqlSessionFactory的openSession方法来创建一个SqlSession，并返回。
     */
    public static SqlSession openSqlSession()
    {
        if (sqlSessionFactory == null)
        {
            if (RandomUtils.nextBoolean())
            {
                initSqlSessionFactory();
            }
            else
            {
                initSqlSessionFactoryInJava();
            }
        }
        return sqlSessionFactory.openSession();
    }
}
